/**
 * @file osal_mem.c
 * @brief 内存管理,支持指定字节对齐,回收时会将相邻的内存块合并
 * @date 2025-09-14
 * @author sqqdfny
 *         sqqdfny@163.com
 */
//==================================================================================================
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>

/**
 * @brief The heap's memory pool is usually defined in the linker script.
 *	      Alternatively, a static array can be used, such as `uint8_t g_heap_buf[CONFIG_MALLOC_SIZE]`.
 *        The alignment granularity must be an integer multiple of the target platform's CPU bit width.
 *        On 32-bit target platforms, it is usually 4 bytes, while on 64-bit platforms, it is usually 8 bytes.
 */
extern uint8_t __heap_Start__;  /* Symbol defined in the linker script */
extern uint8_t __heap_End__;    /* Symbol defined in the linker script */
#define CONFIG_MALLOC_SIZE  (((size_t)&__heap_End__) - ((size_t)&__heap_Start__))

/**
 * @brief If used in a multi-threaded environment, 
 *        please implement the following macros to ensure thread safety. 
 *        Usually, this can be achieved through mutex locks or scheduling locks.
 */
#define OsalMemEnterCritical()  
#define OsalMemExitCritical()   

/**
 * @brief It is required for internal management within the heap. 
 *        On a 32-bit target platform, it is usually 16, 
 *        and on a 64-bit platform, it is usually 32.
 */
#define HEAP_ALIGNMENT_SIZE			16
#define HEAP_HEAD_ALIGNMENT_SIZE	((sizeof(osal_mem_t) < HEAP_ALIGNMENT_SIZE) ? HEAP_ALIGNMENT_SIZE : ((sizeof(osal_mem_t) + HEAP_ALIGNMENT_SIZE - 1) & ~(HEAP_ALIGNMENT_SIZE - 1)))

typedef struct OSAL_MEM_T
{
	struct OSAL_MEM_T *next;
	uint32_t size;
}osal_mem_t;

typedef union
{
	osal_mem_t mem;
	uint8_t reserved[HEAP_HEAD_ALIGNMENT_SIZE];
}osal_mem_head_t;


static osal_mem_head_t *g_pMemHead = NULL;
static size_t g_FreeSize, g_MinFreeSize;

/**
 * @brief 
 * 
 */
#define MemStatisticDec(size)   do{ g_FreeSize -= (size); if(g_MinFreeSize > g_FreeSize) { g_MinFreeSize = g_FreeSize; }}while(0)
#define MemStatisticAdd(size)   do{ g_FreeSize += (size); }while(0)

#define OsLogErr(fmt, ...) printf(fmt, ##__VA_ARGS__)
//==================================================================================================
/**
 * @brief Print the list of free memory blocks to the specified buf.
 *        This function ensures that the returned length does not exceed buf_size.
 * @param buf 
 * @param buf_size 
 * @return size_t   Return the length of the result string. 
 */
size_t OsalMemPrintFreeList(char *buf, size_t buf_size)
{
	osal_mem_head_t *p;
    uint32_t idx = 0;
    size_t size = 0;
    OsalMemEnterCritical();
	if(g_pMemHead)
	{
		p = g_pMemHead;
		do{
			int slen = snprintf(buf + size, buf_size - size, "\t[%04lu]%p:0x%lx\r\n", idx, p, p->mem.size);
			if(slen >= buf_size - size)
			{
				OsalMemExitCritical();
				buf[size] = 0;
				return size;
			}
			size += slen;
			p = (osal_mem_head_t*)(p->mem.next);
			idx ++;
		}while(p);
	}
    OsalMemExitCritical();
    return size;
}

/**
 * @brief 
 * 
 * @return size_t Return the free memory size.
 */
size_t OsalMemGetFreeSize(void)
{
    return g_FreeSize;
}

/**
 * @brief 
 * 
 * @return size_t Return the minimum free memory size.
 */
size_t OsalMemGetMinFreeSize(void)
{
    return g_MinFreeSize;
}

static void _OsalMemInit(void * addr, size_t size_bytes)
{
	uint8_t * addr_tmp;
    if(NULL == addr)
	{
		g_pMemHead = NULL;
		return;
	}

	addr_tmp = (uint8_t *)addr;

	if(((size_t)addr) & (HEAP_ALIGNMENT_SIZE - 1))
	{
		addr_tmp += HEAP_ALIGNMENT_SIZE - (((size_t)addr) & (HEAP_ALIGNMENT_SIZE - 1));
		size_bytes -= (((size_t)addr) & (HEAP_ALIGNMENT_SIZE - 1));
	}

	g_pMemHead = (osal_mem_head_t *)addr_tmp;
	g_pMemHead->mem.next = NULL;
	g_pMemHead->mem.size = size_bytes - sizeof(osal_mem_head_t);
    g_FreeSize = g_pMemHead->mem.size;
    g_MinFreeSize = g_pMemHead->mem.size;
}

void * OsalMemAlloc(size_t size_req)
{
	osal_mem_head_t *p, *prev;
	size_req = (size_req + HEAP_ALIGNMENT_SIZE - 1) & ~(HEAP_ALIGNMENT_SIZE - 1);

    OsalMemEnterCritical();
	if(NULL == g_pMemHead)
	{
        _OsalMemInit((void*)&__heap_Start__, CONFIG_MALLOC_SIZE);
	}

	if(g_pMemHead->mem.size > (size_req + sizeof(osal_mem_head_t) * 4))
	{
		p = g_pMemHead;
		g_pMemHead = (osal_mem_head_t*)(((uint8_t*)(g_pMemHead + 1)) + size_req);
		g_pMemHead->mem.next = p->mem.next;
		g_pMemHead->mem.size = p->mem.size - (size_req + sizeof(osal_mem_head_t));
		p->mem.size = size_req;
		p->mem.next = NULL;
        MemStatisticDec(size_req + sizeof(osal_mem_head_t));
        p ++;
        ((void **)p)[-1] = p - 1;
		OsalMemExitCritical();
		return (p);
	}
	else
	if(g_pMemHead->mem.size >= (size_req))
	{
		p = g_pMemHead;
		g_pMemHead = (osal_mem_head_t*)(g_pMemHead->mem.next);
		p->mem.next = NULL;
        MemStatisticDec((p->mem.size) + sizeof(osal_mem_head_t));
        p ++;
        ((void **)p)[-1] = p -1;
		OsalMemExitCritical();
		return (p);
	}
	else
	{
		if(NULL == g_pMemHead->mem.next)
		{
			OsalMemExitCritical();
			return NULL;
		}
	}

	prev = g_pMemHead;
	p = (osal_mem_head_t*)(prev->mem.next);
	do{
		if(p->mem.size > (size_req + sizeof(osal_mem_head_t) * 4))
		{
			prev->mem.next = (osal_mem_t*)(((uint8_t*)(p + 1)) + size_req);
			prev->mem.next->next = p->mem.next;
			prev->mem.next->size = p->mem.size - (size_req + sizeof(osal_mem_head_t));
			p->mem.size = size_req;
			p->mem.next = NULL;
            p ++;
            ((void **)p)[-1] = p -1;
            MemStatisticDec(size_req + sizeof(osal_mem_head_t));
			OsalMemExitCritical();
			return (p);
		}
		else
		if(p->mem.size >= (size_req))
		{
			prev->mem.next = p->mem.next;
			p->mem.next = NULL;
            MemStatisticDec((p->mem.size) + sizeof(osal_mem_head_t));
            p ++;
            ((void **)p)[-1] = p - 1;
			OsalMemExitCritical();
			return (p);
		}
		else
		{
			prev = p;
			p = (osal_mem_head_t*)(prev->mem.next);
		}
	}while(NULL != p);
	OsalMemExitCritical();
	return NULL;
}

void *OsalMemCalloc(size_t count, size_t size)
{
  void *p;

  /* allocate 'count' objects of size 'size' */
  p = OsalMemAlloc(count * size);
  if (p) {
    /* zero the memory */
    memset(p, 0, count * size);
  }
  return p;
}

void *OsalMemRealloc(void *pMem, size_t newsize)
{
    if(NULL == pMem) return OsalMemAlloc(newsize);

    osal_mem_head_t *p = (osal_mem_head_t*)pMem;
	p --;
    if(p->mem.size >= newsize)
    {
        return pMem;
    }

    void *ptr = OsalMemAlloc(newsize);
    if(ptr)
    {
        memcpy(ptr, pMem, p->mem.size);
        OsalMemFree(pMem);
    }
    return ptr;
}

/**
 * 按指定的字节对齐申请 MEMORY
 * OsalAlignedMalloc() 不会检查 alignment,
 * 调用者必须保证 alignment 为 2^n （1，2，4，8，16，32，64 ...）
*/
void* OsalAlignedMalloc(size_t size, size_t alignment)
{
    assert(0 == (alignment & (alignment - 1)));
    if(alignment <= HEAP_ALIGNMENT_SIZE)
    {
        return OsalMemAlloc(size);
    }

    void* p = OsalMemAlloc(size + alignment);

    if(NULL == p)  { return NULL; }

    if(0 == (((uint32_t)p) & (alignment - 1))) { return p; }

    void **p2 = (void **)((((uint32_t)p) + alignment) & (~(alignment - 1)));
    p2[-1] = ((void **)p)[-1];
    return p2;
}

void OsalMemFree(void* pMem)
{
    if(NULL == pMem) return;

	osal_mem_head_t *p, *prev;
	p = (osal_mem_head_t*)(((void**)pMem)[-1]);
	OsalMemEnterCritical();
    MemStatisticAdd((p->mem.size) + sizeof(osal_mem_head_t));
	if(NULL == g_pMemHead)
	{
		g_pMemHead = p;
		g_pMemHead->mem.next = NULL;
		OsalMemExitCritical();
		return;
	}

	if(p < g_pMemHead)
	{
		p->mem.next = (osal_mem_t*)g_pMemHead;
		if((p->mem.size + sizeof(osal_mem_head_t)) == (((uint8_t*)(p->mem.next)) - ((uint8_t*)p)))
		{
			p->mem.next = g_pMemHead->mem.next;
			p->mem.size += g_pMemHead->mem.size + sizeof(osal_mem_head_t);
		}
		g_pMemHead = p;
		OsalMemExitCritical();
		return;
	}

    prev = g_pMemHead;
	do{
		if(p < (osal_mem_head_t*)(prev->mem.next))
		{
			p->mem.next = prev->mem.next;
			prev->mem.next = (osal_mem_t*)p;
			if((prev->mem.size + sizeof(osal_mem_head_t)) == (((uint8_t*)p) - ((uint8_t*)prev)))
			{
				prev->mem.size += p->mem.size + sizeof(osal_mem_head_t);
				prev->mem.next = p->mem.next;
			}

			if((prev->mem.size + sizeof(osal_mem_head_t)) == (((uint8_t*)prev->mem.next) - ((uint8_t*)prev)))
			{
				prev->mem.size += prev->mem.next->size + sizeof(osal_mem_head_t);
				prev->mem.next = prev->mem.next->next;
			}
			OsalMemExitCritical();
			return;
		}
		prev = (osal_mem_head_t*)prev->mem.next;
	}while(NULL != prev);
	OsalMemExitCritical();
	OsLogErr("unknow mem: %p\n", pMem);
}
//==================================================================================================
//end files
